Skip to content

feat(routing)!: autorouting with policy-derived tiers, preset layer removed - #4543

Open
Yeachan-Heo wants to merge 53 commits into
devfrom
feat/autorouting
Open

feat(routing)!: autorouting with policy-derived tiers, preset layer removed#4543
Yeachan-Heo wants to merge 53 commits into
devfrom
feat/autorouting

Conversation

@Yeachan-Heo

@Yeachan-Heo Yeachan-Heo commented Aug 14, 2026

Copy link
Copy Markdown
Owner

gajae.pr-review-verdict.v1 merge-approved sha256:8efd4c975a9ffbf24b45b3cf0397f16f251598f48a298cd7497310464bdcd23a reviewer:human reviewer-id:probepark evidence:exact-head-349e04c7-autorouting-reconciliation-only

  • high-risk

Scope

Breaking autorouting/preset-removal change for Task subagents. Autorouting remains opt-in (task.autorouting.enabled: false by default), uses the fixed fast/balanced/strong vocabulary, derives tiers from declared provider priority, and removes task.autorouting.preset rather than retaining a compatibility layer.

Owner reconciliation selected the documented rebase-and-repair path. Round 2 transplanted the submitted semantic commits from cd48850135c1b1f7072583dc227346b94b3c6367 onto then-current dev. Round 3 (this head) re-transplanted the identical reviewed lane onto fresh current dev after it advanced again.

Round 3 exact integration state

  • Rebased owned branch onto exact current origin/dev fd700948fc784f46d3427284f8e1da947cd8a10e: 52 semantic commits replayed with zero conflicts; commit subject multiset identical to the approved range.

  • Content equality proven against the approved round-2 diff (fa205dc5e...b9a6992ce, sha256 c760c85872f3883dee5e3ae8ce029c8139056437cb459ab3dc283f9074d6d4c7): full-index diff of old vs new differs in exactly 12 lines — 2 blob index lines where dev advanced package.json/CHANGELOG.md, and 4 hunk-offset lines in selector-controller.ts. All added/removed content hunks are byte-identical.

  • Auto-merged files verified to carry both sides: dev's OIDC release test:release additions plus composer-detach/SdkClient changelog entries alongside all autorouting additions.

  • One reconciliation-only commit 349e04c7d01d7825f62888c76a16b33fe174b6d6: the smart-routing panel test double gains the editorContainer.detachChild no-op that dev's composer-detach hardening (fix(tui): detach reusable composer before clear() at remaining overlay open paths #4687) now requires at SelectorController.showSelector; without it every panel integration test dies on TypeError before asserting. No runtime autorouting behavior changed.

  • Repository: Yeachan-Heo/gajae-code

  • PR: feat(routing)!: autorouting with policy-derived tiers, preset layer removed #4543

  • Base branch: dev

  • Current GitHub base: fd700948fc784f46d3427284f8e1da947cd8a10e

  • Current head: 349e04c7d01d7825f62888c76a16b33fe174b6d6

  • Branch: feat/autorouting

  • Worktree branch: owner/pr-4543-ox-v1

  • Reviewed diff SHA-256 (validator method, git diff --binary --full-index --no-ext-diff fd700948f...349e04c7d): 8efd4c975a9ffbf24b45b3cf0397f16f251598f48a298cd7497310464bdcd23a

  • Prior approval lineage: probepark merge-approved at cd488501 (2026-08-20) and at exact head b9a6992ce (2026-08-22T01:17Z, sha256 c760c858…). This verdict line is intentionally needs-human until a fresh authenticated approval lands on exact head 349e04c7d.

Latest review blockers closed

  • Shared the 256-character selector bound between runtime validation, generated selectors, routing evidence, optional setup schema, tier schema, and regenerated schemas/config.schema.json.
  • Selector grammar now rejects C0/C1 controls and Unicode line separators; generated selectors use the shared validator instead of regex-only validation.
  • Routing-summary tier, effectiveModel, and note values are NFKC-normalized, control/line-separator sanitized, bounded, then XML-escaped before the noEscape task-summary template.
  • Literal colon-bearing model IDs are matched exactly before supported thinking suffix fallback, and the preflight candidate seam reuses the shared thinking suffix parser for :max, :inherit, and :off, with regressions covering both cases.
  • Multimodal catalog entries remain in gate scope unless they are image-generation-only; the shipped text-capable openai/gpt-image-2 and openai-codex/gpt-image-2 entries are explicitly baseline-skip-listed with rationale.
  • Autorouting preflight uses staged publication when durable authority exists and an explicit artifact-only acceptance path when no child session file or artifact manager exists.
  • Removed committed CHANGELOG conflict residue/duplicate history and kept the breaking autorouting notes under Unreleased.
  • Replaced introduced ReturnType<> and inline type imports with concrete/top-level types.

Verification (round 3 lane)

Passed locally on exact head 349e04c7d:

  • bun --cwd=packages/coding-agent run check (biome + tsc clean)
  • Expanded autorouting battery: 504 passing tests across 15 files (task-autorouting unit/preflight/red-team/integration, autorouting boundary/private-seam/provider-order/settings-contract/tier-map/tier-map-gate/generator, acp notice, smart-routing panel integration, model-registry unit+runtime-provider)
  • Adjacent surfaces: 335 passing tests across 11 files plus notifications live-stream 20/20
  • Schema sync: bun run generate-schemas leaves schemas/+types/ clean; check:autorouting-map gate passed (4274 in-scope keys, 3929 baseline skips)
  • Daemon generation guard --validate-current-tree; gjc-state-gates static/runtime/integrity/read all passed
  • ts-build for coding-agent and stats; native addon cargo build
  • check-visible-definitions, verify-g002-gates, rebrand-inventory --strict, default-GJC-definitions suite
  • Fast gate bun scripts/verify-gjc-state-writers.ts --fail: 0 write sites outside sanctioned writers

Known unrelated baseline reds (identical on dev's own CI run #15848, not attributable to this diff): @gajae-code/ai model-manager context-cap metadata, sdk-broker lifecycle e2e exit-134 pair, issue-4508 .gjc home-fixture cleanup, perf-corpus RLM driver admission. These are being fixed dev-side through a separate auxiliary PR so this PR's affected validation can go green without widening the reviewed autorouting contract.

Merge gate

This PR is not represented as merge-ready until an independent authenticated approval is posted on exact head 349e04c7d01d7825f62888c76a16b33fe174b6d6. No release, tag, publish, or main mutation is part of this change.

— gaebal-gajae

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

Draft hold — reconciliation evidence vs current dev and #4561

Recorded heads before any action (exact-head discipline): this draft PR head 874c59f949ea7ec5331da4a13142e7458ce836e9 (30 commits), merge-base with dev 3d9c8f2331, current dev 64c15281691280be7854dac04baeb05188328ef4. PR #4561 head 74156c1580cfb13ea9f5b11ec1195fc2b027f782 (5 commits, APPROVED, MERGEABLE, not merged into dev as of this check).

Reconciliation findings

1. Absorption by dev: none. 0 of the 30 PR commit patch-ids appear in dev since the base; dev contains no autorouting files at all. Nothing here was independently landed.

2. Textual conflicts vs current dev: exactly 1 file, mechanical. A real trial merge of this PR head into dev 64c1528 conflicts only in scripts/telegram-daemon-generation-manifest.json — a single digest hunk for sdk/bus/index.ts:createNotificationsExtension (dev 32faaf97… vs PR ba9b4354…). Cause: dev landed the notification-bus series (6e7c87a769, 5e25bde5b4, 153c73da00) after this PR's last rebase (46 dev commits since). Resolution is regenerating the digest, exactly what 874c59f949 did after the previous rebase. The 3 other both-sides files (sdk/bus/index.ts, sdk/host/session-runtime.ts, CHANGELOG.md) auto-merge cleanly.

3. Overlap with #4561 (oMLX presets): 4 files, 1 real conflict. Trial merge of #4561 onto this PR's head conflicts only in packages/coding-agent/src/task/executor.ts (~line 1713): this PR keeps the explicitThinkingLevel destructure; #4561's 8626a51bab removes it and replaces the effective-thinking gate with resolvedThinkingLevel ?? thinkingLevel for models without thinking config (oMLX). The other 3 overlap files (model-registry.ts, model-selector.ts, task/index.ts) auto-merge.

4. Not supersession — disjoint preset layers. The breaking removal here (5438dd6d2f) deletes only the autorouting preset tier-map layer (AUTOROUTING_PRESETS, AUTOROUTING_PRESET_IDS, AutoroutingPresetId, resolveTierMap, the task.autorouting.preset setting). It does not touch model-profiles.ts. #4561 builds model-profile presets (profile("omlx-qwen36-…"), groupModelProfilesForPresetLanding) plus oMLX provider plumbing (oauth/omlx.ts, port 8000, sentinel filtering) — none of which this PR removes. ModelSelectorViewMode on this head still includes "presets". The two coexist at the product layer: tiers route, profiles stay the manual-selection surface.

5. Residual semantic risk. #4561's thinking-level fallback changes the same resolution path this PR's routed :effort selectors depend on (AUTOROUTING_SELECTOR_PATTERN suffix → resolveModelOverrideWithAuthFallbackexplicitThinkingLeveleffectiveThinkingLevel at executor.ts:1786). Dev's model-resolver.ts:905 already carries the resolveThinkingLevelForModel(...) ?? resolved.thinkingLevel fallback, so the contracts converge — but the executor hunk must be hand-re-resolved when rebasing over 8626a51bab.

Owner decision (blocking)

  • (a) Rebase onto post-feat(ai,config): add oMLX hybrid role-optimized presets #4561 dev — evidence supports this cleanly: 1 mechanical manifest-digest regeneration + 1 hand-re-resolved executor.ts thinking hunk; no supersession.
  • (b) Hold as competing direction — the breaking task.autorouting.preset removal is a product default this lane must not choose.
  • (c) Close as supersedednot supported by evidence (disjoint layers, zero absorption).

This lane stays an explicitly owned draft hold: not marked ready, not pushed, not merged, not closed. All merge trials ran in throwaway worktrees and were aborted; no branch or ref was mutated.

— gaebal-gajae

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

Evidence refresh — dev advanced to 96e718a2, #4561 head moved

Following exact-head discipline: the previous reconciliation comment was bound to dev 64c1528 and #4561 head 74156c158; both moved, so the trial-merge evidence was recomputed from scratch. Both trial merges ran in throwaway worktrees and were aborted; no ref was mutated.

Recomputed state (live REST/API):

Recomputed trial merge, this PR → dev 96e718a2: still exactly 1 conflict filescripts/telegram-daemon-generation-manifest.json, same single digest hunk (createNotificationsExtension: dev 32faaf97… vs this PR ba9b4354…). #4540's session-runtime.ts edits and this PR's edits there auto-merge cleanly (overlap files: CHANGELOG.md, session-runtime.ts only).

Recomputed trial merge, #4561 49e790f4f8 → this PR head: now 2 conflict filestask/executor.ts (same explicitThinkingLevel hunk at ~1713; bb1403448b removes it) and scripts/telegram-daemon-generation-manifest.json (new conflict: #4561's rebase onto dev brought manifest edits). File overlap grew to 5 (added CHANGELOG.md).

Unchanged conclusions, now bound to exact 96e718a2 + 49e790f4f8: absorption still 0/30 (no autorouting files/symbols on dev), preset layers still disjoint (this PR removes only the autorouting tier-map preset symbols and task.autorouting.preset; #4561 builds model-profiles()/oMLX plumbing untouched here). Verdict: partial overlap, not supersession.

Owner decision (blocking, unchanged): (a) rebase onto post-#4561 dev — now 1 mechanical digest regeneration + 1 hand-re-resolved executor.ts hunk + CHANGELOG.md; (b) hold as competing direction; (c) close as superseded — still unsupported. Lane remains an explicitly owned draft hold: not ready, not pushed, not merged, not closed.

— gaebal-gajae

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

OWNER-CONTROLLED DRAFT HOLD — verdict + evidence update (needs-human)

Verdict: NEEDS-HUMAN — owner decision required. Bound to the submitted PR digest via ls-remote (not a local trial): PR head refs/pull/4543/head = 874c59f949ea7ec5331da4a13142e7458ce836e9, recorded base 0f8624d177560461f3da4f02e459bc3d7624c031 (base branch dev), merge-base with dev 3d9c8f2331, 30 commits. Current refs/heads/dev = 96e718a2b0f2a46cad4876bf922886966c6fd0e3 (advanced from 64c1528 via #4540). PR #4561 current head refs/pull/4561/head = 49e790f4f8e6b2a8335a9f568ea46b9662c8e214 (force-moved from 74156c1580, rebased onto 64c1528; open, not draft, not merged into dev).

Conflict / supersession matrix (recomputed against exact 96e718a2 + 49e790f4f8)

Surface Result
Absorption of this PR by dev 0 of 30 commit patch-ids in dev since base; 0 autorouting files/symbols anywhere on 96e718a2
This PR → dev 96e718a2 trial merge 1 conflict file: scripts/telegram-daemon-generation-manifest.json (single digest hunk, createNotificationsExtension: dev 32faaf97… vs PR ba9b4354…); mechanical — regenerate digest as commit 874c59f949 did before. #4540's session-runtime.ts/CHANGELOG.md edits auto-merge with this PR's
#4561 (49e790f4f8) → this PR head trial merge 2 conflict files: task/executor.ts (~line 1713 explicitThinkingLevel hunk; #4561 commit bb1403448b removes it and switches to resolvedThinkingLevel ?? thinkingLevel) and telegram-daemon-generation-manifest.json (new: #4561's rebase brought manifest edits). Overlap set: model-registry.ts, model-selector.ts, executor.ts, task/index.ts, CHANGELOG.md
Supersession None. Preset layers are disjoint: this PR removes only AUTOROUTING_PRESETS, AUTOROUTING_PRESET_IDS, AutoroutingPresetId, resolveTierMap, task.autorouting.preset; #4561 never touches those symbols (0 matches) and builds model-profiles() presets + oMLX provider plumbing, untouched here. Partial overlap, not replacement
Residual semantic risk #4561's thinking-level fallback changes the same resolution path this PR's routed :effort selectors depend on (AUTOROUTING_SELECTOR_PATTERNexplicitThinkingLeveleffectiveThinkingLevel at executor.ts:1786) — the executor hunk must be hand-re-resolved at rebase time

Exact owner choices (pick one)

Lane state (unchanged by this update)

Draft, open, head 874c59f949, not pushed, not marked ready, not merged, not closed. Local worktree fast-forwarded to 96e718a2 (read-only bookkeeping; no push). All trial merges ran in throwaway worktrees, aborted and removed. Resumption of this lane requires fresh owner direction; the agent must not pick (a)/(b)/(c) on its own — choosing is a product-default decision reserved to the owner.

— gaebal-gajae

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

Correction (exact-head discipline): #4561 head moved again after the hold comment posted. Current refs/pull/4561/head = c1b8e021273f553f14ba832ae1feb185e5309ff2 (was 49e790f4f8), now rebased onto dev 96e718a2 (merge-base verified). Trial merge of c1b8e0212 → this PR head recomputed: conflict surface unchanged — same 2 files (task/executor.ts explicitThinkingLevel hunk, telegram-daemon-generation-manifest.json), same 5-file overlap set. #4561 still open and unmerged, so option (a)'s "post-#4561 dev" still does not exist. Every other matrix row and the owner choices stand as posted. Any future resumption of this lane must re-verify #4561's head again before relying on these numbers.

— gaebal-gajae

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

Rebased onto dev at 290b31c110 (101 commits of drift, including the fix(ci) exact-head PR-gate series). Head is now 4b9fea8f4f, behind_dev=0 at push time.

Rebase: 29 of 30 commits replayed with no conflicts. The only conflict was the regenerable telegram digest commit, which was skipped and regenerated against the new base instead of hand-merged.

Two adaptations dev forced:

  • 44 new catalog keys arrived with dev, so check:autorouting-map failed closed on uncurated coverage. They are recorded as baseline skips with an explicit rationale rather than given invented tier/rank data. Gate now passes at 4264 in-scope keys.
  • A stray blank line from the earlier #writeTerminalBreadcrumb conflict resolution reappeared. Last cycle I had folded that formatting fix into the telegram digest commit; skipping that commit this cycle discarded the fix with it, so it is now its own commit and will survive future rebases.

Focused verification on this base: check:types clean, repo-wide biome check exits 0, and 222 tests pass with 0 failures — 98 across the autorouting contract/generator/tier-map/boundary/private-seam suites, 124 across task-routing, preflight, red-team, panel integration, slash-command, ACP notice, tool-catalog, session-staging and SDK/ACP production-path.

Pre-existing dev failures (unchanged conclusion, re-measured against a pristine origin/dev worktree at 290b31c110, same machine, shared node_modules):

  • scripts/telegram-daemon-generation-guard.test.ts fails with ENOENT on packages/coding-agent/test/notifications-topic-registry.test.ts — identical failure on pristine dev.
  • The telegram baseline manifest reports 8 missing commands on both sides (was 5 at the previous base).
  • verify-gjc-sdk-canonicalization reports 27 violations on both sides with an empty sorted diff; every chain is rooted at the dev-owned session-state-sidecar.ts -> tools/descriptors.ts edge, and this branch touches none of the chain roots.

Still a draft for the reason #3764 was closed: the MERGE_READY bar wants green current CI, and those surfaces are red at this base independent of this branch.

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

Draft CI classification at exact head 4b9fea8f4f

This PR remains an owner-controlled Draft at head 4b9fea8f4f0dbdb2025886c97510db4dbf6f55b9, based on current dev 290b31c110889f375c7713b3f64bd10c6338093e. Run 31869555814 is still in progress; current failures classify as follows:

  1. Inherited current-dev failure — not PR-induced

  2. CI transport failure — not a product-test failure

    • shard-5 job 94976620842
    • tests reached completion, but actions/upload-artifact failed during FinalizeArtifact with ECONNRESET
  3. Genuine feat(routing)!: autorouting with policy-derived tiers, preset layer removed #4543 Draft blocker

    • Telegram generation guard job 94976007494
    • packages/coding-agent/src/sdk/bus/index.ts:createNotificationsExtension changes protected Telegram lifecycle behavior without the required strictly higher DAEMON_GENERATION
    • this must be corrected atomically with the repository generation tool before the Draft can become Ready
  4. Expected Draft merge gate

    • bootstrap is closed because this Draft has no merge-approved exact-head contract. It is not merge-ready.

No merge-ready claim is made. No duplicate browser fix belongs in this Draft. Preserve the owner-controlled Draft state; before any Ready transition, reconcile onto green dev after #4575, apply the required Telegram generation bump, rerun exact-head product CI, and obtain fresh review.


[repo owner's gaebal-gajae (clawdbot) 🦞]

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

CI repair pushed to feat/autorouting at d28445e7b9507cd8b27d2966a17f511ea346742e, rebased on current dev 1cc986422ae335bc155740b1060f6be10cfee4b4. Live API and origin/feat/autorouting both confirm that exact head; PR remains Draft.

Fixed product blockers:

  • Telegram lifecycle guard: canonical --fix-generations advanced DAEMON_GENERATION 168→169, updated the topic-registry pin and semantic manifest.
  • Managed OpenAI Responses replay sanitation now removes stale metadata in memory without appending entry_patch records.
  • Presetless model selector accepts minimal settings adapters while preserving the provider-order listener when available; provider onboarding offline refresh/config notification/selector visibility pass.
  • ACP autorouting fixture now registers, awaits, and closes its owned ACP session before broker-root teardown; repeated fresh Bun processes pass without root-recreation cleanup evidence.

Validation on the rebased head:

  • bun --cwd=packages/coding-agent run check
  • generation authority/current-tree validation plus scripts/telegram-daemon-generation-guard.test.ts and topic registry: 135 pass
  • replay + provider onboarding + ACP notice: 33 pass
  • session-storage cohort: 179 pass, 20 skipped
  • autorouting/model-selector cohort: 105 pass
  • ACP notice repeated in three independent Bun processes: 2 pass each.

Run 31869555814 is terminal and stale relative to this head. Classification retained: shard-1 browser default-root is inherited current-dev #4574 / PR #4575; shard-5 is GitHub FinalizeArtifact ECONNRESET; bootstrap is expected for this Draft; evidence-producer and aggregate failures are downstream of the repaired shard/guard failures.


[repo owner’s gaebal-gajae (clawdbot) 🦞]

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

Additional local fresh-process evidence: bun scripts/run-bun-test-files.ts --root=packages/coding-agent --shard=2/8 --timeout=30000 --file-timeout=120000 --concurrency=3 reran 177 files after rebase. The assigned OpenAI replay regression passed. The harness still reported autorouting-boundary-redteam.test.ts, task-autorouting-preflight.test.ts, and a timeout after successful exit for sdk-adapter-dispositions-daemon-cli.test.ts; these were not terminal blockers identified in run 31869555814 and were left unchanged to avoid broadening this owner-controlled CI repair.


[repo owner’s gaebal-gajae (clawdbot) 🦞]

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

A queued affected-path regression exposed an additional PR-scope staging bug before its job terminalized. Fixed and pushed 4ead72d19399cc34d0999910dcb2373fd4752918 to feat/autorouting; live PR API and remote ref agree on that exact Draft head, still based on dev 1cc986422ae335bc155740b1060f6be10cfee4b4.

SessionManager.openStaged() no longer publishes a terminal --continue breadcrumb for an unpublished candidate. The breadcrumb is deferred until staged commit finalization, restoring the no-discovery-residue boundary. The durable C2 boundary test now has its required 30s window so it cannot leak timed-out async work into C3.

Validation: autorouting-boundary-redteam.test.ts 40 pass; task-autorouting-preflight.test.ts 21 pass; bun --cwd=packages/coding-agent run check pass. The prior replacement run 31871546786 is stale for this new head; its completed Telegram guard, Windows Telegram safety, and Windows session-path jobs were green. Bootstrap remains the expected Draft gate.


[repo owner’s gaebal-gajae (clawdbot) 🦞]

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

Terminal Dev CI classification for exact Draft head 4ead72d19399cc34d0999910dcb2373fd4752918 on dev 1cc986422ae335bc155740b1060f6be10cfee4b4: run 31872052977 completed with 74/74 jobs terminal.

Green repaired surfaces include Telegram generation guard (94983909330), Windows Telegram safety (94983909359), Windows session-path regression (94983909355), and all affected autorouting/session/model/ACP targeted jobs.

Failures are classified as:

Explicit owner-controlled Draft dependency hold: #4575 (9af837e5e017bcd2f5a2a802574853fab3c10a09) remains open and unmerged. Rebase #4543 only after it lands and dev advances green; then replacement CI must terminalize on that exact new base. PR remains Draft; no Ready or merge action taken.


[repo owner’s gaebal-gajae (clawdbot) 🦞]

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

Rebased the owner-controlled Draft onto exact dev 2e3ccb5895568ffec709b3adba25bc919d6d248b (#4577) and force-with-lease pushed exact head c9b6e0ef5cf007d866858e5ad06a5f460504a8fe to feat/autorouting. Live PR API and remote ref agree; Draft state remains unchanged. Replacement Dev CI is 31874899362.

#4577 overlap review: Command Code GOAT is retained as its own bundled model profile/provider recommendation and preset. Autorouting remains policy-tier derived and does not restore the removed preset layer; no duplicate profile removal or selector collision was introduced.

Focused validation: routing/model/ACP cohort 117 pass; replay/staging/Telegram cohort 162 pass; Command Code GOAT profile catalog 16 pass; provider onboarding 28 pass; coding-agent check and generation authority/current-tree validation pass.

model-registry.test.ts independently exposes five pre-existing shared-provider/environment cache assertions unrelated to the new GOAT profile mappings; replacement CI is the authority for final classification. Browser dependency hold on #4575 remains in force; no browser code was changed.


[repo owner’s gaebal-gajae (clawdbot) 🦞]

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

Terminal replacement CI classification: run 31874899362 completed 74/74 jobs for exact Draft head c9b6e0ef5cf007d866858e5ad06a5f460504a8fe on dev 2e3ccb5895568ffec709b3adba25bc919d6d248b.

Only product failure is shard-1 94989620493: 176/177 files green; browser-chrome-profile.test.ts default-root refusal is the byte-identical inherited #4574 regression. No autorouting, model-profile, ACP, replay, staging, or Telegram failure is present.

94989061182 bootstrap is the expected Draft gate. Evidence producer 94991903454 and aggregate 94991926255 are downstream only. #4575 remains open pending final merge; this owner-controlled Draft is explicitly held on that dependency. No Ready or merge action taken.


[repo owner’s gaebal-gajae (clawdbot) 🦞]

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

Freshness reset completed after #4575 merged. PR #4543 is rebased onto current dev 45885ea28793b0ace3523272b41f7ad07b1e88e5; exact Draft head is 15f657cd0f8d9fa09388814f121664bf1c93577e, verified by live API and origin/feat/autorouting. Canonical binary diff SHA-256: 33bccfab5c612225a4c734f3ac8afc4b4a10e8a35e7d240b7719c355e3562408.

Semantic overlap review retained #4575 Chrome default-root repair and later detached-managed snapshot work from dev; #4543 preserves policy-derived autorouting tiers and does not restore the removed preset layer. Protected Telegram lifecycle changes were regenerated atomically at generation 170.

Current evidence: routing/model/ACP/staging cohort 83 pass; replay/Telegram/browser cohort 190 pass; coding-agent check, binary build, guard authority/current-tree validation, and affected planner passed. Replacement Dev CI 31880204542 is running.

Honest Draft needs-human verdict: code and local verification are current, but readiness remains owner-controlled and CI must terminalize before any completion assessment. The obsolete #4575 dependency hold is removed; the live Ultragoal G001 ledger records this current-dev hold. Draft remains Draft: no Ready, approval request, merge, close, release, or tag action.


[repo owner’s gaebal-gajae (clawdbot) 🦞]

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

Current exact-head terminal classification for owner-controlled Draft #4543:

  • Base/head: 804314081fe9d3f4d34014d1385f09d8d49a7116 / 4dadf96f1fa6174168b68d8140452d5043f266bb
  • Canonical binary full-index digest: a7b7de3ff1b5e9103954c521381b15d778ae1cc52ba90ced5e6c0b4651b42b68
  • Dev CI: 31885466467 is terminal. Its 72 product/platform jobs succeeded and one platform job was skipped. The sole failure is PR contract bootstrap, expected because the current exact-head verdict is intentionally needs-human; it is not a product failure.
  • Local exact CI-scrubbed root-check replay passed. The final worktree is clean, git diff --check is clean, and the PR verdict preflight validated the current digest.

This remains a Draft, owner-controlled readiness hold. No review, ready transition, merge, close, release, or tag action was taken.


[repo owner's gaebal-gajae (clawdbot) 🦞]

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

Current-head CI failure classification for Draft #4543 (e7b95a4ce957c00d36d532eae62a959ee9295c1b, base cd51365cc270e27dceccfc2c184fadc9c1ddbe18):

  • Canonical affected-plan artifact dev-affected-plan-31889283833 confirms the exact shard command: bun scripts/run-bun-test-files.ts --root=packages/coding-agent --shard=N/8 --timeout=30000 --file-timeout=300000 --concurrency=1.
  • Shard 4 is a success control. Failed shard logs show ReferenceError: Cannot access 'END_PATCH_MARKER' before initialization in packages/coding-agent/src/edit/streaming.ts:85 in shards 1, 2, 3, 6, 7, and 8. The same focused test fails locally at the PR head (bun test packages/coding-agent/test/core/apply-patch.test.ts), and the PR delta does not touch streaming.ts; this is inherited shared module-initialization failure, not an autorouting repair target.
  • Additional inherited shared scraper TDZ traces are present: handleDockerHub in shard 2 and handleYouTube in shard 5, both in packages/coding-agent/src/web/scrapers/index.ts. These files are likewise outside the PR delta.
  • The dedicated shared edit repair is PR fix(coordinator): tolerate unsupported Windows directory sync #4459; it is not merged. Current dev is d332b7890ed95ff71d1a80e00bd4d31809558e69, and its Dev CI 31890215548 is terminal failure, so feat(routing)!: autorouting with policy-derived tiers, preset layer removed #4543 will not copy or substitute that repair. The unchanged autorouting delta must be reconstructed only after an authoritative repaired dev is available.

PR #4543 remains an owner-controlled Draft with needs-human; no readiness, review, merge, close, release, or tag action was taken.


[repo owner's gaebal-gajae (clawdbot) 🦞]

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

Verdict: Request changes

Blocking findings:

  • P1 — packages/coding-agent/src/task/executor.ts, runSubagent() preflight setup and ManagedTaskPersistence.openStagedSession() (~330, ~1740–1780): both paths adopt staged artifact managers, leaving the first attempt root unreachable. Managed autorouting retries can orphan staging roots and leave durable filesystem residue. Centralize manager creation or make generic adoption conditional on !options.managedPersistence; add a lifecycle assertion that exactly one attempt root is used through commit/discard.
  • P2 — packages/coding-agent/scripts/check-autorouting-tier-map.ts, getAutoroutingTierMapGateReport() / checkAutoroutingTierMap(): skip-list rationale, duplicate, malformed, and out-of-scope entries are not validated. Future catalog entries can bypass curation with empty or stale skip entries. Enforce selector grammar, catalog scope, uniqueness, and non-empty rationale.

No tests or gates were run as part of this review.

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

Correction / superseding verdict

The earlier review comment on this PR was based on an incorrect diff scope and is superseded.

A subsequent exact-head review of e7b95a4ce957c00d36d532eae62a9599d3f4d34014d1385f09d8d49a7116 found only the Map.has preflight-fault classification change in packages/coding-agent/src/task/executor.ts plus regression coverage. The change correctly preserves thrown undefined values from the caller's error-transfer path instead of treating them as absent credentials and retrying the candidate. Existing absent-key and non-undefined behavior remains unchanged.

Corrected verdict: Approve / no actionable findings.

No tests or gates were run as part of the read-only review.

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

Final correction / superseding verdict

The previous approval correction was also based on an incomplete/local diff inspection and is superseded. The live PR has 44 commits and 71 changed files at the exact head.

Corrected verdict: Request changes.

  • P1 — packages/coding-agent/src/config/autorouting-generator.ts:164–166: setup provider IDs are accepted with arbitrary casing and provider ordering is case-insensitive, but tier generation matches provider prefixes and catalog keys case-sensitively. A valid setup such as providers: ["OpenAI"] against openai/... catalog keys produces empty fast/balanced/strong tiers.
    • Impact: autorouting can be enabled while silently falling back to manual model resolution for every Task item.
    • Fix: normalize provider IDs consistently at setup/generator/catalog boundaries, or reject non-canonical casing; add a mixed-case regression test covering generated tiers and fingerprints.

No tests or gates were run as part of this read-only review.

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

Additional exact-head finding

  • P2 — packages/coding-agent/src/task/autorouting-contract.ts:48–53: AUTOROUTING_SELECTOR_PATTERN / isValidAutoroutingSelector accept arbitrarily long model IDs, while assertRoutingEvidenceInvariant rejects a non-terminal effectiveModel longer than 256 characters.
    • Impact: a routed custom/provider model ID longer than 256 characters can execute successfully and then fail during routing-evidence finalization, turning a successful task into an error.
    • Fix: enforce the same maximum length in selector grammar/validation, or revise the invariant so accepted selectors cannot be rejected after execution.

This supplements the existing P1 provider-ID case-normalization finding. Overall verdict remains Request changes.

Yeachan-Heo and others added 25 commits August 22, 2026 01:39
…settings

The cleaner lane caught me repeating the exact mistake the terminal critic had
just corrected: the policy-derived golden rebuilt the catalog, spelling map, and
projection inline instead of calling projectCatalogProviderOrder, so it could not
fail if that function broke. It now calls the shipped function, which is what
ModelRegistry.autoroutingProviderOrder delegates to.

The real-registry suite also only assumed the global settings singleton was
uninitialized. A prior test setting modelProviderOrder would have silently
reordered the expected catalog projection and made those assertions accidental,
so the precondition is now reset around each test and asserted outright.

Lore-id: 6a4c0e93
Constraint: a golden must exercise shipped code, never a copy of it
Confidence: high
Scope-risk: narrow
Reversibility: clean
Tested: autorouting-generator 8 pass, autorouting-provider-order 19 pass; removing the spelling restore now fails 5 across both files where it previously failed 4, proving the golden is bound to the real function
…t rebase

Rebasing onto the current dev tip pulled in 44 new catalog keys the autorouting
tier map has never seen, so check:autorouting-map failed closed on uncurated
coverage. Record them as baseline skips with an explicit rationale rather than
inventing tier/rank data nobody reviewed.

Lore-id: 9d1f6b3a
Constraint: an uncurated catalog key is a skip with a rationale, never a guessed tier
Confidence: high
Scope-risk: narrow
Reversibility: clean
Tested: check-autorouting-tier-map gate passed (4264 in-scope keys); autorouting suites 98 pass
…lution

Removing the #writeTerminalBreadcrumb wrapper during the dev rebase left a double
blank line that check:tools rejects. Kept as its own commit rather than folded
into the regenerable telegram digest commit, which a later rebase skips and would
have discarded this fix with it.

Lore-id: 4e7a2b81
Confidence: high
Scope-risk: narrow
Reversibility: clean
Tested: biome check across 3714 files exits 0
Managed session opens must sanitize stale OpenAI Responses metadata in memory without appending durable patches. The autorouting selector must also tolerate minimal settings adapters while retaining its provider-order listener when available.\n\nLore-id: 4543-ci-fixforward-0647\nConstraint: preserve replay safety without rewriting managed transcripts on open\nTested: focused replay, onboarding, session-storage, model-selector, and daemon guard suites\nConfidence: high\nScope-risk: narrow\nReversibility: simple
The autorouting ACP fixture closed only its connection signal, leaving its session adapter alive while broker-root cleanup removed the fixture. Register and await the owned ACP session teardown before releasing the broker lease.\n\nLore-id: 4543-ci-fixforward-0647\nConstraint: fixture roots must remain absent after teardown\nTested: repeated fresh Bun ACP notice regression\nConfidence: high\nScope-risk: narrow\nReversibility: simple
Unpublished autorouting candidates must not replace the terminal continuation breadcrumb. Publish it only when a staged candidate is finalized; give the durable staged regression its required bounded test window.\n\nLore-id: 4543-ci-fixforward-0647\nConstraint: failed candidates leave no durable discovery residue\nTested: autorouting boundary and preflight regressions; coding-agent check\nConfidence: high\nScope-risk: narrow\nReversibility: simple
SDK patches and config CLI writes could bypass nested autorouting validation, while task creation prefiltered credential failures as recoverable absences.\n\nValidate typed autorouting objects at every mutation ingress and leave credential classification to executor preflight so unexpected lookup faults fail closed.\n\nLore-id: pr4543-fixforward\nConstraint: preserve owner-controlled Draft state\nConfidence: high\nScope-risk: focused\nReversibility: revertable\nTested: focused autorouting ingress and preflight suites
Autorouting preflight resolved exact keys against the execution session instead of the distinct credential session.\n\nUse the propagated credential session identity so managed credentials remain available to pinned candidates.\n\nLore-id: pr4543-credential-scope\nConstraint: preserve fail-closed autorouting preflight\nConfidence: high\nScope-risk: focused\nReversibility: revertable\nTested: task-autorouting-preflight
Reject malformed autorouting tier maps before SDK config.patch persists them.\n\nTested: autorouting-settings-contract
Keep truthful missing-credential skips while propagating unexpected lookup errors and using the credential session scope.\n\nTested: autorouting boundary and preflight suites
Defer unexpected TaskTool credential probe failures to executor preflight so routing receipts remain fail-closed and auditable.\n\nTested: autorouting preflight, integration, boundary suites
Root TypeScript validation requires the optional credential session argument to exclude null.\n\nTested: ci-dev-affected root-check
Carry TaskTool credential lookup exceptions into the authoritative preflight ledger instead of retrying and losing one-shot failures.\n\nTested: routing preflight, integration, and boundary suites
Ensure TaskTool transfers an observed credential lookup fault into executor preflight without retrying it.\n\nTested: routing preflight, integration, boundary suites
Use Map presence rather than value truthiness so every captured JavaScript throw reaches terminal preflight evidence.\n\nTested: routing preflight, integration, boundary suites
…nsensitively

Review P1: task.autorouting.setup accepts provider ids in arbitrary casing,
but tier generation matched provider prefixes and catalog keys with exact
case-sensitive startsWith, so a hand-edited providers: ["OpenAI"] against
openai/... keys silently produced empty fast/balanced/strong tiers while
autorouting stayed enabled. Comparison now normalizes both sides while
persistence keeps catalog spelling, and provider de-duplication plus the
allowlist run on normalized ids so two spellings of one provider cannot
double-declare or filter past each other.

Lore-id: a7c3e1f2
Constraint: selectors must stay catalog-spelled in persisted tiers
Tested: mixed-case setup/allowlist/dedup generator regressions
Confidence: high
Scope-risk: narrow
Reversibility: trivial
Review P2: AUTOROUTING_SELECTOR_PATTERN accepted arbitrarily long model ids
while assertRoutingEvidenceInvariant rejects an effectiveModel or
requestedSelector longer than 256 characters, so a routed custom model id
could execute successfully and then fail during routing-evidence
finalization. The shared AUTOROUTING_SELECTOR_MAX_LENGTH constant now
enforces the same bound at validation time, so no accepted selector can be
rejected after execution.

Lore-id: b8d4f2a3
Constraint: invariant in task/types.ts and grammar must share one bound
Tested: over-long selector rejected at config time; 200-char accepted
Confidence: high
Scope-risk: narrow
Reversibility: trivial
…card

Review P1: the managed durable preflight adopted its attempt staging twice --
once inside ManagedTaskPersistence.openStagedSession() and again through the
generic preflightDurable branch in runSubprocessOnce -- leaving the first
manager unreachable from commit/discard so managed autorouting retries could
orphan staging roots. Generic adoption is now conditional on
!options.managedPersistence, and commitStaged/discardStaged fail closed when
a staging manager with a foreign attempt id was adopted over the
publication's own root.

Lore-id: c9e5a3b4
Constraint: fail closed, never silently skip, on root mismatch
Tested: double-root commit and discard regressions; single-root lifecycle
Confidence: high
Scope-risk: moderate
Reversibility: moderate
…keys

Review P2: the tier-map gate accepted skip entries with empty rationales,
malformed keys, out-of-catalog keys, and keys that were both labeled and
skipped, so future catalog additions could bypass curation behind a stale
skip entry. The gate now enforces selector grammar, non-empty rationale,
catalog scope, and label/skip exclusivity, which surfaced five genuinely dead
baseline keys (lowercase minimax-m3 spellings plus a nonexistent minimax-v3)
that are removed rather than carried as permanent skips.

Lore-id: d0f6b4c5
Tested: four new gate rejection cases; gate green at 4272 in-scope keys
Confidence: high
Scope-risk: narrow
Reversibility: trivial
…contracts

The boundary red-team suite pinned the old contract where an over-long tier
selector produced no local validation issue and only the executor's evidence
bounding stood between it and finalization. With the grammar now capped at
the routing-evidence bound, the over-long entry fails closed at validation
time, so the hostile-selector case asserts the early rejection while the
control-only/traversal/homoglyph shapes still flow to executor sanitization,
and the shared skip-projection case stays under the grammar bound so the
16/4 aggregation remains the exercised bound.

Lore-id: e1a8c5d6
Tested: autorouting-boundary-redteam 41 pass
Confidence: high
Scope-risk: narrow
Reversibility: trivial
…ants

Independent-review hardening (architect approve, zero P1s, three
recommendations applied):

- AUTOROUTING_SELECTOR_MAX_LENGTH is now imported and reused by the
  routing-evidence invariant (task/types.ts), the receipt bounding
  (task/receipt.ts), and executor boundedSelector, so the grammar and
  every post-execution bound share one constant by reference instead of
  four coincidental 256 literals.
- commitStaged/discardStaged require strict attempt-root equality while a
  staged publication is uncommitted: an adopted manager that is absent or
  foreign fails closed, closing the released/id-less adoption hole.
- the tier-map gate reports both-labeled-and-skipped keys as invalid skips
  (matching its remediation text) instead of folding them into stale skips.

Lore-id: f2b9c6d7
Tested: tier-map gate green (4272 in-scope, 3927 baseline); gate/preflight/settings suites 52 pass
Confidence: high
Scope-risk: narrow
Reversibility: trivial
Align selector grammar, generated tiers, schema, evidence, and summary rendering so malformed routing data fails closed without prompt-boundary injection. Preserve text-capable multimodal catalog coverage and make sessionless preflight acceptance explicit.

Tested: coding-agent check; focused autorouting, schema, daemon-guard, and affected integration suites

Not-tested: Windows native AVX2 fallback and unrelated packages/ai baseline failures

Confidence: high

Scope-risk: wide

Reversibility: revertable
Do not infer durable preflight publication authority from a shared artifact manager when no session file or managed persistence exists. Align generated selector schemas with the runtime length and role-alias contract.

Tested: coding-agent check; preflight, red-team, settings-contract, and schema-generation tests

Confidence: high

Scope-risk: wide

Reversibility: revertable
Reuse the shared thinking suffix parser when preflighting routed candidates so :max, :inherit, and :off selectors cannot be discarded as snapshot misses.

Tested: coding-agent check; autorouting task, preflight, and red-team suites

Confidence: high

Scope-risk: medium

Reversibility: revertable
…itor container

Dev's composer-detach hardening (#4687) made SelectorController.showSelector
detach the reusable editor before clearing its container, so overlay opens no
longer terminally dispose the composer. The smart-routing panel integration
fixture predates that call site and its editorContainer double lacked
detachChild, so every panel test died on TypeError before asserting anything.
Add the missing no-op to the double, matching the established mock shape in
the sibling model-selector suites.

Reconciliation-only change while rebasing the approved autorouting lane
(b9a6992) onto dev fd70094; runtime autorouting behavior is untouched.

Lore-id: pr4543oxv1
Constraint: preserve the reviewed autorouting contract byte-for-byte
Tested: bun test packages/coding-agent/test/model-selector-smart-routing.integration.test.ts (17/17)
Confidence: high
Scope-risk: none
Reversibility: trivial
@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

Round 3 reconciliation onto current dev fd700948f

dev advanced past the round-2 anchor (fa205dc5e) with 23 commits (OIDC release publishing, release-notes fixes, SDK dispatch-aware request boundary, TUI composer-detach series). The approved lane was re-transplanted:

  • Pushed replacement head: 349e04c7d01d7825f62888c76a16b33fe174b6d6 on feat/autorouting (force-with-lease from b9a6992ce).
  • Zero conflicts. 52 semantic commits replayed; subject multiset identical to the approved range.
  • Content equality proven against the approved diff (c760c858…): old-vs-new full-index diff differs in exactly 12 lines — 2 blob index lines (package.json, CHANGELOG.md advanced by dev) and 4 hunk-offset lines in selector-controller.ts; every added/removed hunk is byte-identical. Auto-merged files verified to preserve both dev's new content and all PR content.
  • One reconciliation-only commit (349e04c7d): the smart-routing panel test double gains the editorContainer.detachChild no-op that fix(tui): detach reusable composer before clear() at remaining overlay open paths #4687's composer-detach hardening now requires; without it the panel integration tests die on TypeError before asserting. No runtime change.
  • New reviewed-diff digest (validator method): sha256:8efd4c975a9ffbf24b45b3cf0397f16f251598f48a298cd7497310464bdcd23a
  • Body verdict rebound to needs-human on the new digest — intentionally, until a fresh authenticated approval lands on this exact head. The prior approvals (cd488501, b9a6992ce) covered byte-identical autorouting content; per the exact-head contract this head needs its own approval.

Local validation on 349e04c7d: expanded autorouting battery 504/504 across 15 files, adjacent surfaces 335+20 green, coding-agent check clean, schema regen sync clean, check:autorouting-map passed (4274 keys / 3929 skips), daemon generation guard passed, gjc-state-gates all four groups passed, coding-agent+stats builds green, native cargo build green, rebrand/G002/visible-definitions gates green, fast gate 0 violations.

Known baseline reds (fail identically on dev's own run #15848 — not attributable to this diff): ai model-manager context-cap, sdk-broker lifecycle e2e exit-134 pair, issue-4508 .gjc home-fixture, perf-corpus RLM admission. A separate dev-side fix PR is in flight so this PR's affected validation can reach green without widening the reviewed contract.

@probepark — requesting your exact-head re-review of 349e04c7d. The reviewer-efficient path: compare git diff --full-index fa205dc5e...b9a6992ce against git diff --full-index fd700948f...349e04c7d (expected: only blob-hash/offset noise plus the single test-double line described above), then approve on the exact head.

@Yeachan-Heo
Yeachan-Heo requested a review from probepark August 22, 2026 02:15

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 349e04c7d0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1809 to +1810
const sessionManager = options.preflightProbe
? SessionManager.inMemory(worktree ?? cwd)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep disposable preflight probes from running extensions

When autorouting evaluates an initial task, this new probe branch creates a complete agent session and continues through extension initialization and session_start emission before deliberately discarding the probe at the provider fence; the durable attempt then repeats the same bootstrap. With subagent extensions enabled, every accepted candidate therefore runs startup hooks twice—and rejected candidates can run them repeatedly—so hooks that write files, mutate settings, send messages, or call external services produce duplicate side effects. Make the probe bootstrap side-effect-free or reuse the probed session for the durable attempt.

Useful? React with 👍 / 👎.

return this.#reportSmartRoutingValidationError(error);
}
return this.#runSmartRoutingIntent("Apply", async () => {
await this.ctx.settings.commitAtomicBatchWithCurrent(() =>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Recheck hand edits inside the locked settings transaction

When another GJC process edits the stored tiers after #assertSmartRoutingNotHandEdited() runs but before this atomic operation acquires the config lock, Apply silently overwrites that edit without the required confirmation. commitAtomicBatchWithCurrent() supplies the lock-current YAML specifically for this case, but the callback ignores it; validate the current tiers/provenance inside that callback (and do the same in Refresh) before returning replacement patches.

Useful? React with 👍 / 👎.

@probepark probepark left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delta review at exact head 349e04c7approved. Prior autorouting approval remains valid.

67 of 71 approved autorouting surface blobs are identical. The four differences are dev package/changelog integration, generic composer detach-before-clear calls, and a one-line test-double update adding detachChild. Core routing, selector resolution, credential preflight, task execution, staged persistence, schemas, and evidence are unchanged.

No concrete wrong-model routing, trust issue, data loss, or durable wedge found.

Reviewed by @probepark — method: exact blob comparison against approved b9a6992c; only reconciliation delta reviewed.

gajae.pr-review-verdict.v1 merge-approved sha256:8efd4c975a9ffbf24b45b3cf0397f16f251598f48a298cd7497310464bdcd23a reviewer:human reviewer-id:probepark evidence:exact-head-349e04c7-autorouting-reconciliation-only

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants